using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;

namespace Zsa.Emulation.PPU
{
    /// <summary>
    /// A 8x8 pattern with a two bit color.
    /// </summary>
    [StructLayout(LayoutKind.Sequential,Size=16,Pack=8)]
    public struct PatternTile
    {
        #region Fields

        #region Constants
        /// <summary>
        /// The tile width.
        /// </summary>
        public const int TileWidth = 8;
        /// <summary>
        /// The tile height.
        /// </summary>
        public const int TileHeight = 8;
        /// <summary>
        /// The pixel size
        /// </summary>
        public const int PatternTilePixelSize = TileWidth*TileHeight;
        /// <summary>
        /// The size of this structure.
        /// </summary>
        //public const int PatternTileByteSize = PatternTilePixelSize/2;
        public static readonly int PatternTileByteSize = Marshal.SizeOf(typeof(PatternTile));
        #endregion Constants

        /// <summary>
        /// The first bit of the color
        /// </summary>
        internal readonly ulong _Tile_Clr1;

        /// <summary>
        /// The 2nd bit of the second color.
        /// </summary>
        internal readonly ulong _Tile_Clr2;

        #endregion Fields

        #region Constructor
        /// <summary>
        /// Initializes a new instance of the <see cref="PatternTile"/> class.
        /// </summary>
        /// <param name="color1">The color1.</param>
        /// <param name="color2">The color2.</param>
        public PatternTile(ulong color1,
            ulong color2)
        {
            _Tile_Clr1 = color1;
            _Tile_Clr2 = color2;

        }

        #endregion Constructor

        #region Methods

        /// <summary>
        /// Sets the byte.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="index">The index.</param>
        public void SetByte(byte value, int index)
        {
            if (index < 0 || index > 15)
            {
                throw new ArgumentOutOfRangeException("index", index, "The index is out of range !");
            }
            unsafe
            {
                fixed (PatternTile* _Tile = &this)
                {
                    byte* _TilePtr = (byte*)_Tile;
                    _TilePtr += index;
                    *_TilePtr = value;
                }
            }
        }

        /// <summary>
        /// Reads the byte.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <returns></returns>
        public byte ReadByte(int index)
        {
            if (index < 0 || index > 15)
            {
                throw new ArgumentOutOfRangeException("index", index, "The index is out of range !");
            }
            byte _TileValue;
            unsafe
            {
                fixed (PatternTile* _Tile = &this)
                {
                    byte* _TilePtr = (byte*)_Tile;
                    _TilePtr += index;
                    _TileValue = * _TilePtr;
                }
            }
            return _TileValue;
        }

        /// <summary>
        /// Gets the pixel.
        /// </summary>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <param name="color">The color.</param>
        /// <returns></returns>
        public byte GetPixel(int x, int y)
        {
            return GetPixel(x, y, 0);
        }

        /// <summary>
        /// Gets the pixel as a byte.
        /// </summary>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <returns></returns>
        public byte GetPixel(int x, int y, byte colorAtr)
        {
            // Make sure in range.
            if (x < 0 || x > TileWidth-1 )
            {
                throw new ArgumentOutOfRangeException("x",string.Format(" The pixels X cordindate must be between 0 and {0}",TileWidth-1));
            }
            if (y < 0 || y > TileHeight-1 )
            {
                throw new ArgumentOutOfRangeException("y", string.Format(" The pixels Y cordindate must be between 0 and {0}", TileHeight - 1));
            }
            // Get the location of the bit.
            int _Adr = (y * TileWidth) + (x);
            // Return the byte.
            return
                (byte)
                (   ((_Tile_Clr1 >> _Adr) & 1) |
                    ((_Tile_Clr2 >> _Adr) & 1) << 1 |
                     colorAtr
                );
        }
        #region Unsafe Draw Methods
        /// <summary>
        /// Set the colors of the tile in an 8 bit image.
        /// </summary>
        /// <param name="startPixel">The start pixel.</param>
        /// <param name="scanWidth">Width of the scan.</param>
        /// <param name="colorAttribute">The color attribute.</param>
        /// <param name="palette">The palette.</param>
        [CLSCompliant(false)]
        internal unsafe void DrawTile(byte* startPixel, int stride, byte pixelSize, 
            byte colorAttribute, PaletteTable palette)
        {
            // The bit of the pixel to get.  1-64 since using longs.
            int _Adr = 63;
            // The color of the 
            int _ColorIndex;
            // Pointer to start of the current row.
            byte* _Buffer = startPixel;
            // Pointers to the colors.
            byte* _R, _G, _B, _A;
            // Set the pointers.
           // palette.SetPointers(_Buffer, _R, _G, _B, _A);

            //The color.
            Color _Color;
            // Loop through the bits.
            for (int _IndexY = 0; _IndexY < TileHeight; _IndexY++)
            {
                // Get the bit.  Add 7 since the bits for each row are backwards??
                _Adr = (_IndexY * TileWidth) + 7;
                // Move to the first row.
                _Buffer = (startPixel + (_IndexY * stride));
                // Loop through the bits.
                for (int _IndexX = 0; _IndexX < TileWidth; _IndexX++)
                {
                    // Get the index of the tile.
                    _ColorIndex = (int)
                        (((_Tile_Clr1 >> _Adr) & 1) |
                        ((_Tile_Clr2 >> _Adr) & 1) << 1 | (ulong)colorAttribute);
                    // Get the color from the palette.
                    _ColorIndex = palette.GetColor(_ColorIndex);
                    if (_ColorIndex > 0)
                    {

                        // Set the pointers for the rgb and alpha.
                        _B = _Buffer + (_IndexX * pixelSize);
                        _G = _B + 1;
                        _R = _G + 1;
                        _A = _R + 1;
                        // Set the color values.

                        *_B = (byte)(_ColorIndex & 0xFF);
                        *_G = (byte)((_ColorIndex >> 8) & 0xFF);
                        *_R = (byte)((_ColorIndex >> 16) & 0xFF);
                        *_A = (byte)((_ColorIndex >> 24) & 0xFF);
                    }
                    // Move to the previous pixel value since rows are backwards.
                    _Adr--;
                }
            }
        }
        #endregion Unsafe Draw Methods

        #endregion Methods

        internal void Flip(SpriteFlags flags)
        {
            unsafe
            {
                // Fix this.
                fixed (PatternTile* _TilePtr = &this)
                {
                    // Get byte ptr to this.
                    byte* _PatternPtr = (byte*)_TilePtr;
                    // Index for current byte.
                    byte _ByteIndex = 0;
                    // Pointers for start and end
                    byte*
                        _StartPtr = _PatternPtr,
                        _EndPtr = _PatternPtr + 8;
                    byte _T;
                    // Flip the tile horizontially.
                   // if (flags == SpriteFlags.FlipHorizontally)
                    if ((flags & SpriteFlags.FlipHorizontally) == SpriteFlags.FlipHorizontally)
                    {
                        // Loop through all 16 bytes and reverse their bits.
                        for ( _ByteIndex = 0; _ByteIndex < 16; _ByteIndex++ )
                        {
                            // Get the byte.
                            _T = *_StartPtr;
                            // Flip the bits in the byte.
                            *_StartPtr = (byte)
                                (((_T & 0x80) >> 7) |
                                ((_T & 0x40) >> 5) |
                                ((_T & 0x20) >> 3) |
                                ((_T & 0x10) >> 1) |
                                ((_T & 0x08) << 1) |
                                ((_T & 0x04) << 3) |
                                ((_T & 0x02) << 5) |
                                ((_T & 0x01) << 7));
                            // Move to the next byte of the tile.
                            _StartPtr++; 
                            
                        }
                       
                    }

                    // If flip the sprite vertically.
                    if ((flags & SpriteFlags.FlipVertically) == SpriteFlags.FlipVertically)
                    {

                        _StartPtr = _PatternPtr;
                        _EndPtr = _PatternPtr + 7;

                        _ByteIndex = 0;

                        for ( _ByteIndex = 0; _ByteIndex < 4; _ByteIndex++ )
                        {
                            // Flip the bytes.
                            *_StartPtr = *(_EndPtr);
                            _StartPtr++; _EndPtr--;
                        }

                        // Move to next set.
                        _StartPtr = _PatternPtr + 8;
                        _EndPtr = _PatternPtr + 15;


                        for (_ByteIndex = 0; _ByteIndex < 4; _ByteIndex++)
                        {
                            // Flip the bytes.
                            *_StartPtr = *(_EndPtr);
                            _StartPtr++; _EndPtr--;
                        }
                    }

                }
            }

        }
    }
    

    ///// <summary>
    ///// A tile made up of 4 tile patterns.
    ///// </summary>
    //[StructLayout(LayoutKind.Sequential,Size=64)]
    //public struct PatternTileSquare
    //{
    //    #region Fields
    //    /// <summary>
    //    /// The pattern tile in top left corner.
    //    /// </summary>
    //    private PatternTile _TileOne;

    //    /// <summary>
    //    /// The pattern tile in the top right corner.
    //    /// </summary>
    //    private PatternTile _TileTwo;

    //    /// <summary>
    //    /// The pattern tile in the bottom left corner.
    //    /// </summary>
    //    private PatternTile _TileThree;

    //    /// <summary>
    //    /// The pattern tile in the bottom right corner.
    //    /// </summary>
    //    private PatternTile _TileFour;
    //    #endregion Fields

    //    public PatternTileSquare(PatternTile tileOne, PatternTile tileTwo,
    //        PatternTile tileThree, PatternTile tileFour)
    //    {
    //        _TileOne = tileOne;
    //        _TileTwo = tileTwo;
    //        _TileThree = tileThree;
    //        _TileFour = tileFour;
    //    }

    //    public byte[] GetColors(byte attribute)
    //    {
    //        byte[] _Bytes = new byte[256];
    //        // Get the bytes.
    //        //Buffer.BlockCopy(_TileOne.GetColors(attribute), 0, _Bytes, 0, 64);
    //        //Buffer.BlockCopy(_TileTwo.GetColors(attribute), 0, _Bytes, 64, 64);
    //        //Buffer.BlockCopy(_TileThree.GetColors(attribute), 0, _Bytes, 128, 64);
    //        //Buffer.BlockCopy(_TileFour.GetColors(attribute), 0, _Bytes, 192, 64);
    //        return _Bytes;
    //    }
    //}





    ///// <summary>
    ///// A tile that has attribute data is ready to be 
    ///// rendered.
    ///// </summary>
    //[StructLayout(LayoutKind.Sequential)]
    //public struct PatternTileGroup
    //{
    //    private PatternTileSquare _TileOne;

    //    private PatternTileSquare _TileTwo;

    //    private PatternTileSquare _TileThree;

    //    private PatternTileSquare _TileFour;

    //    private short _Address;

    //    private byte _AttributeByte;



    //    /// <summary>
    //    /// Gets the colors.
    //    /// </summary>
    //    /// <param name="attribute">The attribute.</param>
    //    /// <returns></returns>
    //    public byte[] GetColors(byte attribute)
    //    {
    //        byte[] _Bytes = new byte[1024];
    //        // Get the bytes from each square.

    //        // First two bytes from attribute are for square one.
    //        byte _Attribute = (byte)(attribute & 0x03);
    //        Buffer.BlockCopy(_TileOne.GetColors(_Attribute), 0, _Bytes, 0, 256);

    //        // Next two bytes from attribute are for square two.
    //        _Attribute = (byte)(attribute & 0x0C);
    //        Buffer.BlockCopy(_TileTwo.GetColors(_Attribute), 0, _Bytes, 256, 256);

    //        // Next two bytes from attribute are for square three.
    //        _Attribute = (byte)(attribute & 0x30);
    //        Buffer.BlockCopy(_TileThree.GetColors(_Attribute), 0, _Bytes, 512, 256);

    //        // Next two bytes from attribute are for square four.
    //        _Attribute = (byte)(attribute & 0xC0);
    //        Buffer.BlockCopy(_TileFour.GetColors(_Attribute), 0, _Bytes, 768, 256);

    //        return _Bytes;
            
    //    }
    //}

    
}
